/*
 * $Id: Utilities.java 42 2013-04-17 15:33:59Z gabakyan $
 * $Author: gabakyan $
 * $Revision: 42 $
 * $Date: 2013-04-17 15:33:59 +0000 (Wed, 17 Apr 2013) $
 *
 * Copyright (c) 2013 Supply Chain Intelligence (SCI), Inc.
 * http://www.scintelligence.com/, Email: info@scintelligence.com
 * All rights reserved.
 *
 * This file is part of Logistics Map.
 *
 * Logistics Map is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 *
 * Logistics Map is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Logistics Map.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.sci.logisticsmap.support;

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.math.BigInteger;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;

/**
 * The utility class which provides helper methods.
 *
 * @author gabakyan@gmail.com (Last modified by $Author: gabakyan $)
 * @version $Revision: 42 $
 */
public final class Utilities {
    /**
     * The filename date marker format.
     */
    private static final DateFormat filenameDateMarkerFormat;

    /**
     * The static initialization block.
     */
    static {
        filenameDateMarkerFormat = new SimpleDateFormat(Constants.FILENAME_DATE_MARKER_PATTERN);
    }

    /**
     * The private constructor.
     */
    private Utilities() {
    }

    /**
     * Returns the current GMT date.
     *
     * @return The current GMT date.
     */
    public static Date getGMTDate() {
        return new Date(getGMTTime());
    }

    /**
     * Returns the given date in GMT.
     *
     * @param date The date.
     *
     * @return The given date in GMT.
     */
    public static Date getGMTDate(Date date) {
        return new Date(getGMTTime(date.getTime()));
    }

    /**
     * Returns the current GMT time in milliseconds.
     *
     * @return The current GMT time in milliseconds.
     */
    public static long getGMTTime() {
        return getGMTTime(System.currentTimeMillis());
    }

    /**
     * Returns the given time in milliseconds in GMT.
     *
     * @param time The time in milliseconds.
     *
     * @return The given time in milliseconds in GMT.
     */
    public static long getGMTTime(long time) {
        TimeZone tz = TimeZone.getDefault();
        return time - tz.getOffset(time);
    }

    /**
     * Returns the line separator.
     *
     * @return The line separator.
     */
    public static String lineSeparator() {
        return Constants.LINE_SEPARATOR;
    }

    /**
     * Encodes the string by MD5 encryption algorithm.
     *
     * @param string The string to be encoded.
     *
     * @return The encoded string.
     */
    public static String encodeStringByMD5(String string) {
        // encrypted string
        String result = null;
        try {
            // encryption algorithm
            MessageDigest md = MessageDigest.getInstance("MD5");

            // string is value to encrypt
            md.update(string.getBytes("UTF-8"), 0, string.length());
            result = new BigInteger(1, md.digest()).toString(16);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * Encodes the string by SHA-256 encryption algorithm.
     *
     * @param string The string to be encoded.
     *
     * @return The encoded string.
     */
    public static String encodeStringBySHA256(String string) {
        StringBuilder resultBuilder = new StringBuilder();
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            md.update(string.getBytes("UTF-8"), 0, string.length());
            byte[] mdBytes = md.digest();
            // let's convert the bytes to hex code...
	        for (int i = 0; i < mdBytes.length; i++) {
                String hexString = Integer.toHexString(0xff & mdBytes[i]);
                if (hexString.length() < 2) {
                    resultBuilder.append('0');
                }
                resultBuilder.append(hexString/*.toUpperCase()*/);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resultBuilder.toString();
	}

    /**
     * Encodes the url parameter.
     *
     * @param parameter The parameter to be encoded.
     *
     * @return The encoded parameter.
     */
    public static String encodeURLParameter(String parameter) {
        try {
        return URLEncoder.encode(parameter, Constants.DEFAULT_ENCODING);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return parameter;
    }

    /**
     * Builds the string.
     *
     * @param values The values to be appended.
     *
     * @return The built string.
     */
    public static <Value> String buildString(Value... values) {
        StringBuilder stringBuilder = new StringBuilder();
        for (Value value : values) {
            stringBuilder.append(value);
        }
        return stringBuilder.toString();
    }

    /**
     * Builds the comma separated string.
     *
     * @param strings The strings to be appended.
     *
     * @return The built comma separated string.
     */
    public static String buildCommaSeparatedString(String[] strings) {
        return buildCommaSeparatedString(strings, Constants.DEFAULT_COMMA_SEPARATOR);
    }

    /**
     * Builds the comma separated string.
     *
     * @param strings The strings to be appended.
     * @param separator The separator.
     *
     * @return The built comma separated string.
     */
    public static String buildCommaSeparatedString(String[] strings, char separator) {
        StringBuilder stringBuilder = new StringBuilder();
        appendCommaSeparatedString(stringBuilder, strings, separator);
        return stringBuilder.toString();
    }

    /**
     * Appends the comma separated strings.
     *
     * @param stringBuilder The string builder.
     * @param strings The strings to be appended.
     *
     * @return The reference to the string builder.
     */
    public static StringBuilder appendCommaSeparatedString(StringBuilder stringBuilder, String[] strings) {
        return appendCommaSeparatedString(stringBuilder, strings, Constants.DEFAULT_COMMA_SEPARATOR);
    }

    /**
     * Appends the comma separated strings.
     *
     * @param stringBuilder The string builder.
     * @param strings The strings to be appended.
     * @param separator The separator.
     *
     * @return The reference to the string builder.
     */
    public static StringBuilder appendCommaSeparatedString(StringBuilder stringBuilder, String[] strings, char separator) {
        for (int i = 0; i < strings.length; i++) {
            iterateAppendCommaSeparatedString(stringBuilder, strings[i], i, separator);
        }
        return stringBuilder;
    }

    /**
     * Appends the comma separated strings.
     *
     * @param stringBuilder The string builder.
     * @param strings The strings to be appended.
     *
     * @return The reference to the string builder.
     */
    public static StringBuilder appendCommaSeparatedString(StringBuilder stringBuilder, Iterable<String> strings) {
        return appendCommaSeparatedString(stringBuilder, strings, Constants.DEFAULT_COMMA_SEPARATOR);
    }

    /**
     * Appends the comma separated strings.
     *
     * @param stringBuilder The string builder.
     * @param strings The strings to be appended.
     * @param separator The separator.
     *
     * @return The reference to the string builder.
     */
    public static StringBuilder appendCommaSeparatedString(StringBuilder stringBuilder, Iterable<String> strings, char separator) {
        int i = 0;
        for (String string : strings) {
            iterateAppendCommaSeparatedString(stringBuilder, string, i++, separator);
        }
        return stringBuilder;
    }

    /**
     * Writes the comma separated strings.
     *
     * @param writer The character stream writer.
     * @param strings The strings to be wrote.
     */
    public static void writeCommaSeparatedString(Writer writer, String[] strings) throws IOException {
        writeCommaSeparatedString(writer, strings, Constants.DEFAULT_COMMA_SEPARATOR);
    }

    /**
     * Writes the comma separated strings.
     *
     * @param writer The character stream writer.
     * @param strings The strings to be wrote.
     * @param separator The separator.
     */
    public static void writeCommaSeparatedString(Writer writer, String[] strings, char separator) throws IOException {
        for (int i = 0; i < strings.length; i++) {
            iterateWriteCommaSeparatedString(writer, strings[i], i, separator);
        }
    }

    /**
     * Writes the comma separated strings.
     *
     * @param writer The character stream writer.
     * @param strings The strings to be wrote.
     */
    public static void writeCommaSeparatedString(Writer writer, Iterable<String> strings) throws IOException {
        writeCommaSeparatedString(writer, strings, Constants.DEFAULT_COMMA_SEPARATOR);
    }

    /**
     * Writes the comma separated strings.
     *
     * @param writer The character stream writer.
     * @param strings The strings to be wrote.
     * @param separator The separator.
     */
    public static void writeCommaSeparatedString(Writer writer, Iterable<String> strings, char separator) throws IOException {
        int i = 0;
        for (String string : strings) {
            iterateWriteCommaSeparatedString(writer, string, i++, separator);
        }
    }

    /**
     * Returns the file extension.
     *
     * @param filename The filename.
     *
     * @return The file extension.
     */
    public static String getFileExtension(String filename) {
        int index;
        if ((index = filename.lastIndexOf('.')) != -1) {
            return filename.substring(index + 1);
        }
        return "";
    }

    /**
     * Formats the file name date marker.
     *
     * @param dateMarker
     *         The file name date marker id.
     *
     * @return The formatted file name date marker.
     */
    public static String formatFilenameDateMarker(Date dateMarker) {
        return filenameDateMarkerFormat.format(dateMarker);
    }

    /**
     * Builds the byte array http entity.
     *
     * @param contentType The content type.
     * @param content The content byte array.
     *
     * @return The built byte array http entity.
     */
    public static HttpEntity<byte[]> buildByteArrayHttpEntity(String contentType, byte[] content) {
        return buildByteArrayHttpEntity(contentType, content, null);
    }

    /**
     * Builds the byte array http entity.
     *
     * @param contentType The content type.
     * @param content The content byte array.
     * @param filename The filename.
     *
     * @return The built byte array http entity.
     */
    public static HttpEntity<byte[]> buildByteArrayHttpEntity(String contentType, byte[] content, String filename) {
        HttpHeaders header = new HttpHeaders();
        // sets the content type
        header.setContentType(MediaType.valueOf(contentType));
        // sets the content disposition as an attachment
        if (filename != null) {
            header.set("Content-Disposition", buildString("attachment; filename=", filename));
        }
        // sets the content length
        header.setContentLength(content.length);

        return new HttpEntity<byte[]>(content, header);
    }

    /**
     * Iterates the comma separated strings appendix.
     *
     * @param stringBuilder The string builder.
     * @param string The string to be appended.
     * @param i The string index.
     * @param separator The separator.
     */
    private static void iterateAppendCommaSeparatedString(StringBuilder stringBuilder, String string, int i, char separator) {
        if (i > 0) {
            stringBuilder.append(separator);
            //stringBuilder.append(' ');
        }
        if (string.indexOf(separator) == -1) {
            stringBuilder.append(string);
        } else {// if contains separator quote the string
            stringBuilder.append("\"").append(string).append("\"");
        }
    }

    /**
     * Iterates the comma separated strings writing.
     *
     * @param writer The character stream writer.
     * @param string The string to be wrote.
     * @param i The string index.
     * @param separator The separator.
     */
    private static void iterateWriteCommaSeparatedString(Writer writer, String string, int i, char separator) throws IOException {
        if (i > 0) {
            writer.write(separator);
            //writer.write(' ');
        }
        if (string.indexOf(separator) == -1) {
            writer.write(string);
        } else {// if contains separator quote the string
            writer.write("\"");
            writer.write(string);
            writer.write("\"");
        }
        writer.write(string);
    }
}
